﻿using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.XPath;

namespace Netfraction.Common.DataStorage
{
    public class XmlDataStore : DataStore, IDataStore
    {
        private XPathDocument xPathDoc;
        private XmlDocument xDoc;

        /// <summary>
        /// Creates a new XmlDataStore instance.
        /// </summary>
        /// <param name="owner"></param>
        public XmlDataStore(DataSource owner)
            : base(owner)
        {
            this.DataStoreType = DataStoreType.XmlFile;
        }

        /// <summary>
        /// Creates a new XmlDataStore instance and the specified data store. Does not overwrite an existing store.
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="storeName"></param>
        public XmlDataStore(DataSource owner, string storeName)
            : base(owner)
        {
            this.DataStoreType = DataStoreType.XmlFile;
            this.DataStoreName = storeName;

            Create(storeName, false);
        }

        public bool Create(string name, bool overwrite)
        {
            if (!CheckBasePath(name))
                return false;

            if (!overwrite && File.Exists(Path.Combine(this.BasePath, name)))
                return true;

            if (!Directory.Exists(Path.GetDirectoryName(Path.Combine(this.BasePath, name))))
                Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(this.BasePath, name)));

            FileStream f = File.Create(Path.Combine(this.BasePath, name));
            f.Close();
            this.DataStoreName = name;

            return true;
        }

        public XPathNavigator CreateNavigator(bool canEdit)
        {
            if (!CheckFileExists())
                return null;

            if ((xDoc == null && canEdit) || (xPathDoc == null && !canEdit))
            {
                try
                {
                    return canEdit ? CreateXmlDocumentBasedNavigator() : CreateXPathDocumentBasedNavigator();
                }
                catch (FileNotFoundException fnfex)
                {
                    Netfraction.Common.Logging.LogManager.WriteEntry(fnfex.Message, Netfraction.Common.Logging.LogEntryType.Error, Netfraction.Common.Logging.LogEventId.XmlStoreException, Netfraction.Common.Logging.LogLevel.Detailed);
                    return null;
                }
                catch (Exception ex)
                {
                    Netfraction.Common.Logging.LogManager.WriteEntry(ex.Message, Netfraction.Common.Logging.LogEntryType.Error, Netfraction.Common.Logging.LogEventId.XmlStoreException, Netfraction.Common.Logging.LogLevel.Detailed);
                    return null;
                }
            }

            return null;
        }

        private XPathNavigator CreateXmlDocumentBasedNavigator()
        {
            xPathDoc = null;
            xDoc = new XmlDocument();
            xDoc.Load(new FileStream(this.FullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read));
            return (xDoc == null ? null : xDoc.CreateNavigator());
        }

        private XPathNavigator CreateXPathDocumentBasedNavigator()
        {
            xDoc = null;
            xPathDoc = new XPathDocument(new FileStream(this.FullPath, FileMode.Open, FileAccess.Read, FileShare.Read));
            return (xPathDoc == null ? null : xPathDoc.CreateNavigator());
        }

        /// <summary>
        /// Returns an XmlTextWriter for the Xml document. Warning: The current file is overwritten! If you want to append to a large set of data, consider an SqlDataStore or TextDataStore.
        /// </summary>
        /// <returns></returns>
        public XmlTextWriter CreateWriter()
        {
            if (!CheckFileExists())
                return null;

            try
            {
                return new XmlTextWriter(new FileStream(this.FullPath, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8);
            }
            catch (Exception ex)
            {
                Netfraction.Common.Logging.LogManager.WriteEntry(ex.Message, Netfraction.Common.Logging.LogEntryType.Error, Netfraction.Common.Logging.LogEventId.XmlStoreException, Netfraction.Common.Logging.LogLevel.Detailed);
                return null;
            }
        }

        /// <summary>
        /// Saves the document if it was opened in edit mode via CreateNavigator.
        /// </summary>
        /// <returns></returns>
        public bool Save()
        {
            if (xDoc == null)
                return false;

            try
            {
                xDoc.Save(CreateWriter());
                return true;
            }
            catch (Exception ex)
            {
                Netfraction.Common.Logging.LogManager.WriteEntry(ex.Message, Netfraction.Common.Logging.LogEntryType.Error, Netfraction.Common.Logging.LogEventId.XmlStoreException, Netfraction.Common.Logging.LogLevel.Detailed);
            }

            return false;
        }

        public bool IsEmpty()
        {
            FileInfo f = new FileInfo(this.FullPath);

            if (f.Length == 0)
                return true;
            else
                return false;
        }

        private bool CheckFileExists()
        {
            if (string.IsNullOrEmpty(this.DataStoreName))
                return false;

            if (!File.Exists(this.FullPath) && !Create(this.DataStoreName, false))
                return false;

            return true;
        }

        public bool Delete()
        {
            if (this.DataStoreName == string.Empty)
                return false;

            xPathDoc = null;
            xDoc = null;

            File.Delete(this.FullPath);

            return File.Exists(this.FullPath);
        }
    }
}
